package net.simpleframework.workflow.schema;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import net.simpleframework.core.Version;
import net.simpleframework.util.StringUtils;
import net.simpleframework.workflow.WorkflowException;

import org.dom4j.Element;

/**
 * 这是一个开源的软件，请在LGPLv3下合法使用、修改或重新发布。
 * 
 * @author 陈侃(cknet@126.com, 13910090885)
 *         http://code.google.com/p/simpleframework/
 *         http://www.simpleframework.net
 */
public class ProcessNode extends Node {
	private String packageName;

	private Version version;

	private String author;

	/*
	 * 含义：判断模型是流程实例共享，还是为每一个流程实例复制一个拷贝
	 */
	private boolean instanceShared;

	private AbstractProcessStartupType startupType;

	private Collection<VariableNode> variables;

	private Map<String, Node> nodes;

	public ProcessNode(final Element dom4jElement) {
		super(dom4jElement, null);
	}

	public String getPackageName() {
		return packageName;
	}

	public void setPackageName(final String packageName) {
		this.packageName = packageName;
	}

	public Version getVersion() {
		if (version == null) {
			version = new Version(1, 0, 0);
		}
		return version;
	}

	public void setVersion(final Version version) {
		this.version = version;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(final String author) {
		this.author = author;
	}

	public boolean isInstanceShared() {
		return instanceShared;
	}

	public void setInstanceShared(final boolean instanceShared) {
		this.instanceShared = instanceShared;
	}

	public AbstractProcessStartupType getStartupType() {
		if (startupType == null) {
			startupType = new AbstractProcessStartupType.Manual(null, this);
		}
		return startupType;
	}

	public void setStartupType(final AbstractProcessStartupType startupType) {
		this.startupType = startupType;
	}

	public Node getNodeById(final String id) {
		return nodes.get(id);
	}

	public Node removeNode(final String id) {
		return nodes.remove(id);
	}

	public <T extends Node> T addNode(final Class<T> nodeClass) {
		return addNode(null, nodeClass);
	}

	public <T extends Node> T addNode(final String id, final Class<T> nodeClass) {
		try {
			final T t = nodeClass.getConstructor(Element.class, ProcessNode.class).newInstance(null,
					this);
			t.setId(id);
			nodes.put(t.getId(), t);
			return t;
		} catch (final Exception e) {
			throw WorkflowException.wrapException(e);
		}
	}

	public Collection<Node> nodes() {
		return nodes.values();
	}

	public TransitionNode addTransition(final AbstractTaskNode from, final AbstractTaskNode to) {
		return addTransition(null, from, to);
	}

	public TransitionNode addTransition(final String id, final AbstractTaskNode from,
			final AbstractTaskNode to) {
		final TransitionNode transition = addNode(id, TransitionNode.class);
		transition.setFrom(from.getId());
		transition.setTo(to.getId());
		return transition;
	}

	public Collection<VariableNode> variables() {
		if (variables == null) {
			variables = new ArrayList<VariableNode>();
		}
		return variables;
	}

	private StartNode startNode;

	public StartNode startNode(final String id) {
		if (startNode == null) {
			startNode = new StartNode(null, this);
			if (StringUtils.hasText(id)) {
				startNode.setId(id);
			}
			nodes.put(startNode.getId(), startNode);
		}
		return startNode;
	}

	public StartNode startNode() {
		return startNode(null);
	}

	private EndNode endNode;

	public EndNode endNode(final String id) {
		if (endNode == null) {
			endNode = new EndNode(null, this);
			if (StringUtils.hasText(id)) {
				endNode.setId(id);
			}
			nodes.put(endNode.getId(), endNode);
		}
		return endNode;
	}

	public EndNode endNode() {
		return endNode(null);
	}

	@Override
	public void syncElement() {
		super.syncElement();
		getStartupType().syncElement();

		final Element element = getElement().element("nodes");
		element.setContent(null);
		for (final Node node : nodes()) {
			node.syncElement();
			addElement(element, node);
		}
		for (final VariableNode variable : variables()) {
			variable.syncElement();
		}
	}

	@Override
	public void parseElement() {
		super.parseElement();
		Element ele = getElement().element("startup-type");
		if (ele != null) {
			Element ele2;
			if ((ele2 = ele.element("manual")) != null) {
				setStartupType(new AbstractProcessStartupType.Manual(ele2, this));
			} else if ((ele2 = ele.element("email")) != null) {
				setStartupType(new AbstractProcessStartupType.Email(ele2, this));
			}
		}

		nodes = new LinkedHashMap<String, Node>();
		Iterator<?> it = children("nodes");
		while (it.hasNext()) {
			ele = (Element) it.next();
			final String name = ele.getName();
			if ("user-node".equals(name)) {
				final UserNode userNode = new UserNode(ele, this);
				nodes.put(userNode.getId(), userNode);
			} else if ("transition".equals(name)) {
				final TransitionNode transition = new TransitionNode(ele, this);
				nodes.put(transition.getId(), transition);
			} else if ("start-node".equals(name)) {
				startNode = new StartNode(ele, this);
				nodes.put(startNode.getId(), startNode);
			} else if ("end-node".equals(name)) {
				endNode = new EndNode(ele, this);
				nodes.put(endNode.getId(), endNode);
			}
		}

		final Collection<VariableNode> variables = variables();
		it = children("variables");
		while (it.hasNext()) {
			variables.add(new VariableNode((Element) it.next(), this));
		}
	}
}
